from mcp.server.fastmcp import FastMCP
from typing import List, TypedDict
import math
import matplotlib.pyplot as plt
import pandas as pd

# 定义输出类型
class meanStats(TypedDict):
    样本量: int
    最大值: int
    最小值: int
    平均值: float

class medianStats(TypedDict):
    样本量: int
    最大值: int
    最小值: int
    中位数: float

class VarStats(TypedDict):
    样本量: int
    最大值: int
    最小值: int
    方差: float
    标准差: float

class StdStatsKurtosis(TypedDict):
    样本量: int
    最大值: int
    最小值: int
    峰度: float

class StdStatsSkewness(TypedDict):
    样本量: int
    最大值: int
    最小值: int
    偏度: float


# Create an MCP server
mcp = FastMCP("Statistical Analysis Service")

# 统计工具实现
@mcp.tool()
def calculate_mean(data: List[float]) -> meanStats:
    """计算均值统计量"""
    return {
        "样本量": len(data),
        "最大值": int(max(data)),
        "最小值": int(min(data)),
        "平均值": sum(data) / len(data)
    }

@mcp.tool()
def calculate_median(data: List[float]) -> medianStats:
    """计算中位数统计量"""
    sorted_data = sorted(data)
    n = len(sorted_data)
    mid = n // 2
    median = (sorted_data[mid - 1] + sorted_data[mid]) / 2 if n % 2 == 0 else sorted_data[mid]
    return {
        "样本量": n,
        "最大值": int(max(data)),
        "最小值": int(min(data)),
        "中位数": median
    }

@mcp.tool()
def analyze_csv(file_path: str, column: str) -> dict:
    """分析CSV文件数据"""
    if not file_path:
        raise ValueError("请提供CSV文件路径")
    if not column:
        raise ValueError("请提供要分析的列名")
    
    df = pd.read_csv(file_path)
    if column not in df.columns:
        raise ValueError(f"列 '{column}' 不存在于CSV文件中")
        
    data = df[column].tolist()
    return {
        "mean": calculate_mean(data),
        "median": calculate_median(data)
    }

@mcp.tool()
def analyze_xlsx(file_path: str, sheet_name: str, column: str) -> dict:
    """分析XLSX文件数据"""
    if not file_path:
        raise ValueError("请提供XLSX文件路径")
    if not sheet_name:
        raise ValueError("请提供工作表名称")
    if not column:
        raise ValueError("请提供要分析的列名")
    
    try:
        df = pd.read_excel(file_path, sheet_name=sheet_name, engine='openpyxl')
        
        if column not in df.columns:
            raise ValueError(f"列 '{column}' 不存在于工作表 '{sheet_name}' 中")
            
        data = df[column].dropna().tolist()
        if not data:
            raise ValueError("提取的数据为空，请检查文件内容")
            
        if not all(isinstance(x, (int, float)) for x in data):
            raise TypeError("数据包含非数值类型，请确保所选列只包含数字")
            
        return {
            "mean": calculate_mean(data),
            "median": calculate_median(data)
        }
    except FileNotFoundError:
        raise ValueError(f"文件 '{file_path}' 不存在")
    except ValueError as e:
        raise ValueError(f"数据处理错误: {str(e)}")
    except Exception as e:
        raise RuntimeError(f"读取XLSX文件时发生错误: {str(e)}")

@mcp.tool()
def generate_boxplot(data: List[float]) -> str:
    """生成箱线图"""
    plt.figure(figsize=(8, 6))
    # 确保中文显示正常
    plt.rcParams["font.family"] = ["SimHei"]
    plt.rcParams['axes.unicode_minus'] = False  # 解决负号显示问题
    # 自定义箱线图样式
    whiskerprops = dict(color='blue')
    capprops = dict(color='blue')
    medianprops = dict(color='red', linewidth=2)
    flierprops = dict(marker='o', color='red', alpha=0.5)
    plt.boxplot(data, whiskerprops=whiskerprops,
                capprops=capprops, medianprops=medianprops, flierprops=flierprops, patch_artist=True)

    # 设置箱体颜色
    boxes = plt.gca().findobj(match=plt.Rectangle)
    for box in boxes:
        box.set_facecolor('lightblue')
        box.set_edgecolor('blue')
    plt.title('数据分布箱线图')
    plt.ylabel('数值')
    plt.savefig('e:\\25-30实训\\git_shixun_process\\项目仓库\\2025-07-02\\demo\\demo\\boxplot.png', dpi=300, bbox_inches='tight')
    plt.close()
    return 'boxplot.png'

@mcp.tool()
def calculate_variance(data: List[float]) -> VarStats:
    """计算方差和标准差"""
    mean = sum(data) / len(data)
    variance = sum((x - mean) ** 2 for x in data) / len(data)
    std_dev = math.sqrt(variance)
    return {
        "样本量": len(data),
        "最大值": int(max(data)),
        "最小值": int(min(data)),
        "方差": variance,
        "标准差": std_dev
    }

@mcp.tool()
def calculate_skewness(data: List[float]) -> StdStatsSkewness:
    """计算偏度"""
    mean = sum(data) / len(data)
    std_dev = math.sqrt(sum((x - mean) ** 2 for x in data) / len(data))
    skewness = (sum((x - mean) ** 3 for x in data) / len(data)) / (std_dev ** 3)
    return {
        "样本量": len(data),
        "最大值": int(max(data)),
        "最小值": int(min(data)),
        "偏度": skewness
    }

@mcp.tool()
def calculate_kurtosis(data: List[float]) -> StdStatsKurtosis:
    """计算峰度"""
    mean = sum(data) / len(data)
    std_dev = math.sqrt(sum((x - mean) ** 2 for x in data) / len(data))
    kurtosis = (sum((x - mean) ** 4 for x in data) / len(data)) / (std_dev ** 4) - 3
    return {
        "样本量": len(data),
        "最大值": int(max(data)),
        "最小值": int(min(data)),
        "峰度": kurtosis
    }




@mcp.tool()
def add(a: float, b: float) -> float:
    """Add two numbers"""
    return a + b

@mcp.tool()
def subtract(a: float, b: float) -> float:
    """Subtract two numbers"""
    return a - b

@mcp.tool()
def multiply(a: float, b: float) -> float:
    """Multiply two numbers"""
    return a * b

@mcp.tool()
def divide(a: float, b: float) -> float:
    """Divide two numbers"""
    if b == 0:
        raise ValueError("Cannot divide by zero")
    return a / b

@mcp.tool()
def square_root(x: float) -> float:
    """Calculate square root of a number"""
    if x < 0:
        raise ValueError("Cannot calculate square root of negative number")
    return x ** 0.5

@mcp.resource("greeting://{name}")
def get_greeting_resource(name: str) -> str:
    """Get a greeting resource for the specified name"""
    return f"Greetings resource for {name}: Hello and welcome!"

if __name__ == "__main__":
    mcp.run(transport="stdio")